package dnl.toolbox.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * General reflection utility methods that are concerned with fields.
 * 
 * @author Daniel Or
 * @version 1.0
 */
public class Fields {

	public static Class<?> getFieldType(Class<?> c, String propertyName, boolean allowSuper) {
		Class<?> clax = c;
		while (!Object.class.equals(clax)) {
			Field f = null;
			try {
				f = clax.getDeclaredField(propertyName);
			} catch (SecurityException e) {
			} catch (NoSuchFieldException e) {
			}
			if (f != null) {
				return f.getType();
			}
			if (!allowSuper) {
				return null;
			}
			clax = clax.getSuperclass();
		}
		return null;
	}

	/**
	 * Gets all final static values in the given class which are of the given
	 * type.<br>
	 * (if the given type is <code>Object.class</code> ALL fields will be
	 * returned.)
	 * 
	 * @param c
	 *            the class of which all consts are desired.
	 * @param type
	 *            the filtering type.
	 * @return a <code>List</code> containing all desired values.
	 */
	public static List<Object> getFinalStatics(Class<?> c, Class<?> type) {
		Field[] flds = c.getFields();
		List<Object> al = new ArrayList<Object>();

		for (int i = 0; i < flds.length; i++) {
			int mod = flds[i].getModifiers();

			if (Modifier.isPublic(mod) && Modifier.isStatic(mod) && Modifier.isFinal(mod)
					&& flds[i].getType().equals(type)) {
				try {
					al.add(flds[i].get(null));
				} catch (Exception ex) {
					// will not be added if exception.
				}
			}
		}

		return al;
	}

	public static Field[] getInstanceFields(Class<?> c) {
		Field[] flds = c.getFields();
		List<Field> al = new ArrayList<Field>();

		for (int i = 0; i < flds.length; i++) {
			int mod = flds[i].getModifiers();

			if (!Modifier.isStatic(mod) && !Modifier.isFinal(mod)) {
				al.add(flds[i]);
			}
		}

		return al.toArray(new Field[] {});
	}

	/**
	 * 
	 * @param c
	 * @param name
	 * @return
	 */
	public static Object getStaticField(Class<?> c, String name) {
		Field[] flds = c.getFields();

		for (int i = 0; i < flds.length; i++) {
			int mod = flds[i].getModifiers();

			if (Modifier.isPublic(mod) && Modifier.isStatic(mod) && flds[i].getName().equals(name)) {
				try {
					return flds[i].get(null);
				} catch (Exception ex) { // improbable, were talking of a
					// static field.
					return null;
				}
			}
		}
		return null;
	}

	public static Map<String, Object> getObjectFieldValues(Object obj) {
		Map<String, Object> result = new HashMap<String, Object>();
		Field[] instanceFields = obj.getClass().getDeclaredFields();
		for (Field field : instanceFields) {
			int mod = field.getModifiers();
			if (Modifier.isStatic(mod)) {
				continue;
			}
			String fieldName = field.getName();
			field.setAccessible(true);
			try {
				Object value = field.get(obj);
				result.put(fieldName, value);
			} catch (Exception e) {
				continue;
			}
		}
		return result;
	}
}